package hn.sigit.logic.viewer;

import hn.sigit.dao.hnd.administrative.HND_PropertyDAO;
import hn.sigit.dao.hnd.cadastre.HND_LayerDAO;
import hn.sigit.dao.hnd.cadastre.HND_SpatialZoneDAO;
import hn.sigit.dao.hnd.special.HND_CounterDAO;
import hn.sigit.logic.general.GeneralHelper;
import hn.sigit.logic.general.ResourceBundleHelper;
import hn.sigit.logic.general.impl.GeneralHelperImpl;
import hn.sigit.logic.viewer.toolbox.BorderHelper;
import hn.sigit.logic.viewer.toolbox.CreateZoneViewerCallable;
import hn.sigit.logic.viewer.toolbox.LayerHelper;
import hn.sigit.logic.viewer.toolbox.MergeHelper;
import hn.sigit.logic.viewer.toolbox.ParcelDataHelper;
import hn.sigit.logic.viewer.toolbox.RRRHelper;
import hn.sigit.logic.viewer.toolbox.SearchHelper;
import hn.sigit.logic.viewer.toolbox.SplitViewerCallable;
import hn.sigit.logic.viewer.toolbox.ViewerCallable;
import hn.sigit.model.commons.IParcel;
import hn.sigit.model.commons.IProperty;
import hn.sigit.model.commons.ISpatialZone;
import hn.sigit.model.hnd.administrative.HND_Property;
import hn.sigit.model.hnd.cadastre.HND_Layer;
import hn.sigit.model.hnd.cadastre.HND_Parcel;
import hn.sigit.model.hnd.cadastre.HND_SpatialZone;
import hn.sigit.model.hnd.special.HND_CounterType;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.model.SelectItem;

import org.jboss.seam.Component;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.AutoCreate;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.Polygon;
import com.vividsolutions.jts.io.WKTWriter;

@Name("interactiveViewerHelper")
@Scope(value=ScopeType.CONVERSATION)
@AutoCreate
public class InteractiveViewerHelper implements Serializable {
	private static final long serialVersionUID = -5485155887832623658L;
	private static final String DEFAULT_LAYER = "sigit:hnd_parcel_polygon";
	
	public static final String NAME = "interactiveViewerHelper";
	
	private Long presentationId;
	
	private ISpatialZone selectedZone;
	private HND_Layer selectedLayer;
	private IProperty selectedProperty;
	private ISpatialZone zoomedInZone;
	
	private Double envelopeLeft = 0.0;
	private Double envelopeBottom = 0.0;
	private Double envelopeRight = 0.0;
	private Double envelopeTop = 0.0;

	private SearchHelper searchHelper;
	private RRRHelper rrrHelper;
	private BorderHelper borderHelper;
	private MergeHelper mergeHelper;
	private ParcelDataHelper parcelDataHelper;
	private LayerHelper layerHelper;
	
	private ViewerCallable splitCallable = new SplitViewerCallable();
	private ViewerCallable createZoneCallable = new CreateZoneViewerCallable();
	
	private ResourceBundleHelper resBundle;
	
	private String highlightedSpatialUnitIds;
	
	private List<HND_SpatialZone> highlightedSpatialZoneList;
	private List<HND_Layer> layerList;
	private List<SelectItem> layerItems;
	
	
	private Boolean showSearchSpatialZoneToolbox = true;
	private Boolean showRightsToolbox = true;
	private Boolean showRestrictionsToolbox = true;
	private Boolean showResponsibilitiesToolbox = true;
	private Boolean showDataToolbox = true;
	private Boolean showSpatialAnalysisToolbox = true;
	private Boolean showSaveModificationButtons = false;
	private Boolean showHelpToolbox = true;
	private Boolean showTitle = true;
	private Boolean drawParcelLayerOnly = false;
	
	private boolean allowEditing = false;
	private BorderEditMode borderEditMode = BorderEditMode.SPLIT_MERGE;
	
	private HND_LayerConverter layerConverter;
	
	private List<Geometry> createdZoneGeoms;
	private final WKTWriter wktWriter = new WKTWriter();
	
	private List<String> additionalReRenderIds;
	private String reRenderList;
	private final String defaultReRenderList = "zoomFun,grpSelectedZone,panelRights,panelRestrictions,panelResponsibilities,panelData,splitParcelPanel,mergeParcelPanel";

	
	@In(create=true)
	private GeneralHelper generalHelper;
	
	@In(create=true)
	private MapHelper mapHelper;
	
	

	public InteractiveViewerHelper() {
		this.selectedLayer = findLayerByFullQualifiedName(DEFAULT_LAYER);

		this.mapHelper = (MapHelper) Component.getInstance("mapHelper", true);
		this.mapHelper.setSelectedLayerFQName(this.selectedLayer.getFullQualifiedName());
		this.additionalReRenderIds = new ArrayList<String>();
	}
	
	public ResourceBundleHelper getResBundle() {
		if (resBundle == null) {
			resBundle = getGeneralHelper().getResBundle();
		}
		return resBundle;
	}

	private GeneralHelper getGeneralHelper() {
		if (generalHelper == null) {
			this.generalHelper = (GeneralHelper) Component.getInstance(GeneralHelper.NAME, true);
		}
		return generalHelper;
	}
	
	public Long getPresentationId() {
		return presentationId;
	}
	public void setPresentationId(Long presentationId) {
		this.presentationId = presentationId;
	}

	public SearchHelper getSearchHelper() {
		if (searchHelper == null) {
			searchHelper = new SearchHelper(this);
		}
		return searchHelper;
	}

	public RRRHelper getRrrHelper() {
		if (rrrHelper == null) {
			rrrHelper = new RRRHelper(this);
		}
		return rrrHelper;
	}

	public BorderHelper getBorderHelper() {
		if (borderHelper == null) {
			borderHelper = new BorderHelper(this);
		}
		return borderHelper;
	}

	public MergeHelper getMergeHelper() {
		if (mergeHelper == null)
			mergeHelper = new MergeHelper(this);
		return mergeHelper;
	}

	public ParcelDataHelper getParcelDataHelper() {
		if (parcelDataHelper == null)
			parcelDataHelper = new ParcelDataHelper(this);
		return parcelDataHelper;
	}

	public LayerHelper getLayerHelper() {
		if (layerHelper == null) {
			layerHelper = new LayerHelper(this);
		}
		return layerHelper;
	}
	
	public ViewerCallable getSplitCallable() {
		return splitCallable;
	}
	public void setSplitCallable(ViewerCallable splitCallable) {
		this.splitCallable = splitCallable;
	}
	
	public ViewerCallable getCreateZoneCallable() {
		return createZoneCallable;
	}
	public void setCreateZoneCallable(ViewerCallable createZoneCallable) {
		this.createZoneCallable = createZoneCallable;
	}

	public String getMapOptions() {
		return mapHelper.mapOptions(drawParcelLayerOnly);
	}
	
	public boolean isAllowEditing() {
		return allowEditing;
	}
	public void setAllowEditing(boolean allowEditing) {
		this.allowEditing = allowEditing;
	}

	public BorderEditMode getBorderEditMode() {
		return borderEditMode;
	}
	public void setBorderEditMode(BorderEditMode borderEditMode) {
		this.borderEditMode = borderEditMode;
	}

	public ISpatialZone getSelectedZone() {
		return selectedZone;
	}
	public void setSelectedZone(ISpatialZone selectedZone) {
		if (this.selectedZone != selectedZone) {
			this.selectedZone = selectedZone;
			this.selectedProperty = null;
			setZoomedInZone(selectedZone);
			//mark the RRR fields to be updated/refreshed
			if (rrrHelper != null) {
				rrrHelper.setParcelRightsList(null);
				rrrHelper.setParcelRestrictionsList(null);
				rrrHelper.setParcelResponsibilitiesList(null);
				rrrHelper.setRrrSet(null);
				rrrHelper.setProperty(null);
			}
		}
	}

	public IProperty getSelectedProperty() {
		if (selectedProperty == null) {
			if (getSelectedZone() != null)
				selectedProperty = getSelectedZone().getProperty();
		}
		return selectedProperty;
	}
	public void setSelectedProperty(IProperty selectedProperty) {
		this.selectedProperty = selectedProperty;
	}

	public ISpatialZone getZoomedInZone() {
		return zoomedInZone;
	}
	public void setZoomedInZone(ISpatialZone zoomedInZone) {
		if (this.zoomedInZone == zoomedInZone) return;
		
		this.zoomedInZone = zoomedInZone;
		Polygon p = (Polygon) zoomedInZone.getShape();
		if (p != null) {
			Polygon pEnvelope = (Polygon) p.getEnvelope();
			Coordinate[] coords = pEnvelope.getCoordinates();
			
			envelopeLeft = envelopeBottom = envelopeRight = envelopeTop = null;
			if (coords.length > 1) {
				Double minx = Double.MAX_VALUE;
				Double miny = Double.MAX_VALUE;
				Double maxx = Double.MIN_VALUE;
				Double maxy = Double.MIN_VALUE;
				for (Coordinate c : coords) {
					if (c.x < minx) minx = c.x;
					if (c.x > maxx) maxx = c.x;
					if (c.y < miny) miny = c.y;
					if (c.y > maxy) maxy = c.y;
				}
				envelopeLeft = minx;
				envelopeBottom = miny;
				envelopeRight = maxx;
				envelopeTop = maxy;
			}
		}
	}

	public IParcel getSelectedParcel() {
		if (selectedZone != null && selectedZone instanceof IParcel)
			return (IParcel) selectedZone;
		return null;
	}

	public Double getEnvelopeLeft() {
		return envelopeLeft;
	}
	public Double getEnvelopeBottom() {
		return envelopeBottom;
	}
	public Double getEnvelopeRight() {
		return envelopeRight;
	}
	public Double getEnvelopeTop() {
		return envelopeTop;
	}


	public String getSelectedZoneText() {
		if (getSelectedZone() != null)
			return generalHelper.getZoneUsefulName(getSelectedZone());
		else
			return generalHelper.getResBundle().loadMessage("txt.no_zone_or_parcel_selected");
	}
	
		
	public String getPrimaryLandUseName() {
		if (getSelectedZone() == null
				|| getSelectedZone().getPrimaryLandUse() == null)
			return "";
		
		return getSelectedZone().getPrimaryLandUse().getFullName();
	}
	public void setPrimaryLandUseName(String landUseName) {
	}
	
	public String getSecondaryLandUseName() {
		if (getSelectedZone() == null
				|| getSelectedZone().getSecondaryLandUse() == null)
			return "";
		return getSelectedZone().getSecondaryLandUse().getFullName();
	}
	public void setSecondaryLandUseName(String landUseName) {
	}

	public String getHighlightedSpatialUnitIds() {
		return highlightedSpatialUnitIds;
	}
	public void setHighlightedSpatialUnitIds(String highlightedSpatialUnitIds) {
		this.highlightedSpatialUnitIds = highlightedSpatialUnitIds;
		highlightedSpatialZoneList = null;
	}
	
	public List<HND_SpatialZone> getHighlightedSpatialZoneList() {
		HND_SpatialZone spatialZone;
		if (highlightedSpatialZoneList == null) {
			highlightedSpatialZoneList = new ArrayList<HND_SpatialZone>();
			if (highlightedSpatialUnitIds != null) {
				String[] suIds = highlightedSpatialUnitIds.split(",");
				for (String str : suIds) {
					if (!str.trim().equals("")) {
						spatialZone = HND_SpatialZoneDAO.loadSpatialZoneById(Long.parseLong(str));
						if (spatialZone != null)
							highlightedSpatialZoneList.add(spatialZone);
					}
				}
			}
		}
		return highlightedSpatialZoneList;
	}
	

	public List<HND_Layer> getLayerList() {
		if (layerList == null) {
			layerList = HND_LayerDAO.loadLayers(true);
		}
		return layerList;
	}
	
	public List<SelectItem> getLayerItems() {
		if (layerItems == null) {
			layerItems = new ArrayList<SelectItem>();
			for (HND_Layer layer : getLayerList())
				if (!drawParcelLayerOnly || layer.isParcelLayer())
					layerItems.add(new SelectItem(layer, layer.getName()));
		}
		return layerItems;
	}
	
	public HND_Layer getSelectedLayer() {
		return selectedLayer;
	}
	public void setSelectedLayer(HND_Layer selectedLayer) {
		this.selectedLayer = selectedLayer;
	}

	public Boolean getShowSearchSpatialZoneToolbox() {
		return showSearchSpatialZoneToolbox;
	}
	public void setShowSearchSpatialZoneToolbox(Boolean showSearchSpatialZoneToolbox) {
		if (showSearchSpatialZoneToolbox == null) return;
		this.showSearchSpatialZoneToolbox = showSearchSpatialZoneToolbox;
	}

	public Boolean getShowRightsToolbox() {
		return showRightsToolbox;
	}
	public void setShowRightsToolbox(Boolean showRightsToolbox) {
		if (showRightsToolbox == null) return;
		this.showRightsToolbox = showRightsToolbox;
	}

	public Boolean getShowRestrictionsToolbox() {
		return showRestrictionsToolbox;
	}
	public void setShowRestrictionsToolbox(Boolean showRestrictionsToolbox) {
		if (showRestrictionsToolbox == null) return;
		this.showRestrictionsToolbox = showRestrictionsToolbox;
	}

	public Boolean getShowResponsibilitiesToolbox() {
		return showResponsibilitiesToolbox;
	}
	public void setShowResponsibilitiesToolbox(Boolean showResponsibilitiesToolbox) {
		if (showResponsibilitiesToolbox == null) return;
		this.showResponsibilitiesToolbox = showResponsibilitiesToolbox;
	}

	public Boolean getShowDataToolbox() {
		return showDataToolbox;
	}
	public void setShowDataToolbox(Boolean showDataToolbox) {
		if (showDataToolbox == null) return;
		this.showDataToolbox = showDataToolbox;
	}

	public Boolean getShowSpatialAnalysisToolbox() {
		return showSpatialAnalysisToolbox;
	}
	public void setShowSpatialAnalysisToolbox(Boolean showSpatialAnalysisToolbox) {
		if (showSpatialAnalysisToolbox == null) return;
		this.showSpatialAnalysisToolbox = showSpatialAnalysisToolbox;
	}

	public Boolean getShowSaveModificationButtons() {
		return showSaveModificationButtons;
	}
	public void setShowSaveModificationButtons(Boolean showSaveModificationButtons) {
		if (showSaveModificationButtons == null) return;
		this.showSaveModificationButtons = showSaveModificationButtons;
	}
	
	public Boolean getDrawParcelLayerOnly() {
		return drawParcelLayerOnly;
	}
	public void setDrawParcelLayerOnly(Boolean drawParcelLayerOnly) {
		if (drawParcelLayerOnly == null) return;
		this.drawParcelLayerOnly = drawParcelLayerOnly;
	}

	public Boolean getShowHelpToolbox() {
		return showHelpToolbox;
	}
	public void setShowHelpToolbox(Boolean showHelpToolbox) {
		if (showHelpToolbox == null) return;
		this.showHelpToolbox = showHelpToolbox;
	}

	public Boolean getShowTitle() {
		return showTitle;
	}
	public void setShowTitle(Boolean showTitle) {
		if (showTitle == null) return;
		this.showTitle = showTitle;
	}

	
	public String getZoneSelectionRerenderString() {
		StringBuffer bs = new StringBuffer();
		
		if (showRightsToolbox) bs.append("panelRights,");
		if (showRestrictionsToolbox) bs.append("panelRestrictions,");
		if (showResponsibilitiesToolbox) bs.append("panelResponsibilities,");
		if (showDataToolbox) bs.append("panelData,");
		if (showSpatialAnalysisToolbox) bs.append("panelSpatialAnalysis,grpOperand1,");
		
		bs.deleteCharAt(bs.length()-1);
		
		return bs.toString();
	}

	public boolean isEditing() {
		return getRrrHelper().isEditing() || getParcelDataHelper().isEditing();
	}

	public long getFieldTabCounter() {
		return HND_CounterDAO.nextValue(HND_CounterType.FIELD_TAB);
	}
	
	public void addAditionalReRenderId(String id) {
		if (!additionalReRenderIds.contains(id)) {
			additionalReRenderIds.add(id);
			reRenderList = null;
		}
	}
	
	public String getReRenderList() {
		if (reRenderList == null) {
			StringBuilder sb = new StringBuilder(defaultReRenderList);
			for (String str : additionalReRenderIds) {
				sb.append(',');
				sb.append(str);
			}
			reRenderList = sb.toString();
		}
		return reRenderList;
	}


	public GeneralHelper getGeneralHelperInstance() {
		if (generalHelper == null) {
			generalHelper = new GeneralHelperImpl();
		}
		return generalHelper;
	}
	
	
	private HND_Layer findLayerByFullQualifiedName(String fullQualifiedName) {
		for (HND_Layer layer : getLayerList())
			if (layer.getFullQualifiedName().equals(fullQualifiedName))
				return layer;
		return null;
	}
	
	public HND_LayerConverter getLayerConverter() {
		if (layerConverter == null) {
			layerConverter = new HND_LayerConverter();
		}
		return layerConverter;
	}
	
	public class HND_LayerConverter implements Converter {
		@Override
		public Object getAsObject(FacesContext context, UIComponent component, String value) {
			for (HND_Layer layer : getLayerList())
				if (layer.getIID() == Long.parseLong(value))
					return layer;
			return null;
		}

		@Override
		public String getAsString(FacesContext context, UIComponent component, Object value) {
			return String.valueOf(((HND_Layer) value).getIID());
		}
	}
	
	
	public List<Geometry> getCreatedZoneGeoms() {
		if (createdZoneGeoms == null) {
			createdZoneGeoms = new ArrayList<Geometry>();
		}
		return createdZoneGeoms;
	}
	public void setCreatedZoneGeoms(List<Geometry> createdZoneGeoms) {
		this.createdZoneGeoms = createdZoneGeoms;
	}
	public String getLastGeomWKT() {
		if (getCreatedZoneGeoms().size() == 0) return "";
		
		Geometry lastGeom = getCreatedZoneGeoms().get(getCreatedZoneGeoms().size()-1);
		return wktWriter.write(lastGeom);
	}
	public String resetCreatedZones() {
		getCreatedZoneGeoms().clear();
		return null;
	}
	public String saveCreatedZones() {
		ISpatialZone firstSZ = null;
		boolean first = true;
		for (Geometry geom : getCreatedZoneGeoms()) {
			if (geom instanceof Polygon) {
				ISpatialZone newSZ;
				if (getSelectedLayer().isParcelLayer()) {
					newSZ = new HND_Parcel();
					((HND_Parcel) newSZ).setFieldTab(getFieldTabCounter());
				}
				else {
					newSZ = new HND_SpatialZone();
				}
				
				if (first) {
					firstSZ = newSZ;
					first = false;
				}
				
				//TODO: Verificar que el begin lifespan date se haga ahorita o hasta aprovar...
				//newSZ.setBeginLifespanVersion(new Date());
				newSZ.setShape(geom);
				
				IProperty newProperty = new HND_Property();
				newProperty.getSpatialUnits().add(newSZ);
				newSZ.getBaunits().add(newProperty);
				
				HND_SpatialZoneDAO.save(newSZ);
				HND_PropertyDAO.save(newProperty);
			}
		}
		
		if (firstSZ != null)
			setSelectedZone(firstSZ);
		
		getCreatedZoneGeoms().clear();
		
		return null;
	}


	public static enum BorderEditMode {
		NONE,
		SPLIT_MERGE,
		CREATE,
		MIXED
	}
}
